/***************************************************************************
 * Copyright (C) 2010
 * by Dimok
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any
 * damages arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any
 * purpose, including commercial applications, and to alter it and
 * redistribute it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you
 * must not claim that you wrote the original software. If you use
 * this software in a product, an acknowledgment in the product
 * documentation would be appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and
 * must not be misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source
 * distribution.
 *
 * for WiiXplorer 2010
 ***************************************************************************/
#include "../Controls/Application.h"
#include "../Themes/CTheme.h"
#include "../FileOperations/DirList.h"
#include "PlayList.hpp"

#define PAGE_SIZE	8

PlayList::PlayList()
	: GuiFrame(0, 0)
{
	SetVisible(false);
	listOffset = 0;
	selectedItem = 0;
	forceReload = false;
	Minimized = true;
	width = 332;
	height = 352;

	btnSoundClick = NULL;
	btnSoundOver = NULL;
	menu_selectionData = NULL;
	playlistImgData = NULL;
	trigA = NULL;
	playlistImg = NULL;
	PlayListBtn = NULL;
	AddSoundBtn = NULL;
	scrollbar = NULL;
}

PlayList::~PlayList()
{
	Hide();
	Save();
	InternalClearList();
}

bool PlayList::Save()
{
	char filepath[1024];
	snprintf(filepath, sizeof(filepath), "%s", Settings.ConfigPath);

	while(filepath[strlen(filepath)-1] == '/')
		filepath[strlen(filepath)-1] = '\0';

	strcat(filepath, "/SaveGameGX_PlayList.txt");

	FILE * file = fopen(filepath, "wb");
	if (!file)
		return false;

	fprintf(file, "# SaveGameGX Music Playlist\n");
	fprintf(file, "# Note: This file is automatically generated\n\n");

	for(int i = 0; i < (int)FileList.size(); i++)
		fprintf(file, "%s\n", FileList.at(i).c_str());

	fclose(file);

	return true;
}

bool PlayList::LoadList()
{
	char filepath[1024];
	char line[1024];
	snprintf(filepath, sizeof(filepath), "%s", Settings.ConfigPath);
	
	while(filepath[strlen(filepath)-1] == '/')
		filepath[strlen(filepath)-1] = '\0';
	
	strcat(filepath, "/SaveGameGX_PlayList.txt");
	
	FILE * file = fopen(filepath, "rb");
	if (!file)
		return false;
	
	while (fgets(line, sizeof(line), file))
	{
		if (line[0] == '#') continue;
		
		std::string sline(line);
		
		if(sline.size() < 4) continue;
		
		size_t pos = 0;
		
		do
		{
			pos = sline.find_first_of('\n');
			
			if(pos != std::string::npos)
				sline.erase(pos, 1);
		}
		while(pos != std::string::npos);
		
		do
		{
			pos = sline.find_first_of('\r');
			
			if(pos != std::string::npos)
				sline.erase(pos, 1);
		}
		while(pos != std::string::npos);
		
		InternalAddEntrie(sline);
	}
	fclose(file);
	
	return true;
}

void PlayList::OnButtonClick(GuiButton *sender, int pointer UNUSED, const POINT &p UNUSED)
{
	Application::Instance()->SetGuiInputUpdate(false);
	
	if(sender == PlayListBtn)
	{
		if(Minimized && parentElement)
		{
			Minimized = false;
			
			for(int i = 0; i < PAGE_SIZE && i < (int)FileList.size(); i++)
				ListBtn[i]->SetState(STATE_DEFAULT);
			AddSoundBtn->SetState(STATE_DEFAULT);
			
			SetEffect(EFFECT_MOVE | EFFECT_MOVE_VERT, 20, GetTop()-parentElement->GetTop()-height+83);
			while(IsAnimated())
				Application::Instance()->updateEvents();
		}
		else if(parentElement)
		{
			SetEffect(EFFECT_MOVE | EFFECT_MOVE_VERT, 20, GetTop()-parentElement->GetTop()+height-83);
			while(IsAnimated())
				Application::Instance()->updateEvents();
			
			for(int i = 0; i < PAGE_SIZE; i++)
				ListBtn[i]->SetState(STATE_DISABLED);
			AddSoundBtn->SetState(STATE_DISABLED);
			
			Minimized = true;
		}
	}
	
	Application::Instance()->SetGuiInputUpdate(true);
}

void PlayList::OnButtonListClick(GuiButton *sender, int pointer UNUSED, const POINT &p UNUSED)
{
	if(sender == AddSoundBtn)
	{
		ItemClicked(this, -1);
		return;
	}
	
	for(u32 i = 0; i < ListBtn.size(); i++)
	{
		if(sender == ListBtn[i])
		{
			ItemClicked(this, i);
			break;
		}
	}
}

void PlayList::Show()
{
	btnSoundClick = Resources::GetSound("button_click.wav");
	btnSoundOver = Resources::GetSound("button_over.wav");

	ListChanged.connect(this, &PlayList::OnPlayListChange);
	
	trigA = new GuiTrigger;
	trigA->SetSimpleTrigger(-1, WiiControls.ClickButton | ClassicControls.ClickButton << 16, GCControls.ClickButton);

	menu_selectionData = Resources::GetImageData("browser_bg_selection.png");

	playlistImgData = Resources::GetImageData("music_playlist.png");
	playlistImg = new GuiImage(playlistImgData);
	playlistImg->SetBounds(cutBoundsRect);
	Append(playlistImg);

	scrollbar = new Scrollbar(240, Scrollbar::CUSTOMMODE);
	scrollbar->SetAlignment(ALIGN_RIGHT | ALIGN_TOP);
	scrollbar->SetPosition(-30, 50);
	scrollbar->SetScrollSpeed(Settings.ScrollSpeed);
	scrollbar->SetBounds(cutBoundsRect);
	scrollbar->listChanged.connect(this, &PlayList::OnListChange);
	Append(scrollbar);

	AddSoundBtn = new GuiButton(26, 17);
	AddSoundBtn->SetPosition(27, 13);
	AddSoundBtn->SetSoundClick(btnSoundClick);
	AddSoundBtn->SetSoundOver(btnSoundOver);
	AddSoundBtn->SetTrigger(trigA);
	AddSoundBtn->Clicked.connect(this, &PlayList::OnButtonListClick);
	AddSoundBtn->SetState(STATE_DISABLED);
	Append(AddSoundBtn);
	
	PlayListBtn = new GuiButton(26, 17);
	PlayListBtn->SetPosition(134, 13);
	PlayListBtn->SetSoundClick(btnSoundClick);
	PlayListBtn->SetSoundOver(btnSoundOver);
	PlayListBtn->SetTrigger(trigA);
	PlayListBtn->Clicked.connect(this, &PlayList::OnButtonClick);
	Append(PlayListBtn);

	width = playlistImgData->GetWidth();
	height = playlistImgData->GetHeight();

	for(int i = 0; i < PAGE_SIZE; i++)
	{
		int size = i;
		
		ListBtnImgOver.resize(size+1);
		ListBtnImgOver[size] = new GuiImage(menu_selectionData);
		ListBtnImgOver[size]->SetAlignment(ALIGN_CENTER | ALIGN_MIDDLE);
		ListBtnImgOver[size]->SetScaleX(0.6);
		ListBtnImgOver[size]->SetPosition(-20, 0);
		
		ListBtnTxt.resize(size+1);
		ListBtnTxt[size] = new GuiText((char*) NULL, 18, thColor("r=0 g=0 b=0 a=255 - MusicPlayer play list text color"));
		ListBtnTxt[size]->SetAlignment(ALIGN_LEFT | ALIGN_MIDDLE);
		ListBtnTxt[size]->SetMaxWidth(width - 110, DOTTED);
		
		ListBtnTxtOver.resize(size+1);
		ListBtnTxtOver[size] = new GuiText((char*) NULL, 18, thColor("r=0 g=0 b=0 a=255 - MusicPlayer play list text color"));
		ListBtnTxtOver[size]->SetAlignment(ALIGN_LEFT | ALIGN_MIDDLE);
		ListBtnTxtOver[size]->SetMaxWidth(width - 130, SCROLL_HORIZONTAL);
		
		ListBtn.resize(size+1);
		ListBtn[size] = new GuiButton(width-100, 20);
		ListBtn[size]->SetLabel(ListBtnTxt[size]);
		ListBtn[size]->SetLabelOver(ListBtnTxtOver[size]);
		ListBtn[size]->SetImageOver(ListBtnImgOver[size]);
		ListBtn[size]->SetPosition(40, 62+30*i+3);
		ListBtn[size]->SetTrigger(trigA);
		ListBtn[size]->SetSoundClick(btnSoundClick);
		ListBtn[size]->SetBounds(cutBoundsRect);
		ListBtn[size]->SetRumble(Settings.BrowserRumble);
		ListBtn[size]->StateChanged.connect(this, &PlayList::OnListStateChange);
		ListBtn[size]->Clicked.connect(this, &PlayList::OnButtonListClick);
		ListBtn[size]->SetState(STATE_DISABLED);
		
		Append(ListBtn[size]);
	}
	
	SetVisible(true);
	
	scrollbar->SetEntrieCount(FileList.size());
	scrollbar->SetPageSize(PAGE_SIZE);
	scrollbar->SetRowSize(0);
	scrollbar->SetSelected(0, 0);
	
	ListChanged();
}

void PlayList::Hide()
{
	RemoveAll();

	Resources::Remove(btnSoundClick);
	Resources::Remove(btnSoundOver);
	Resources::Remove(menu_selectionData);
	Resources::Remove(playlistImgData);

	for(u32 i = 0; i < ListBtn.size(); i++)
	{
		delete ListBtn[i];
		delete ListBtnTxt[i];
		delete ListBtnTxtOver[i];
		delete ListBtnImgOver[i];
	}
	ListBtn.clear();
	ListBtnTxt.clear();
	ListBtnTxtOver.clear();
	ListBtnImgOver.clear();

	if(trigA)
		delete trigA;
	if(playlistImg)
		delete playlistImg;
	if(AddSoundBtn)
		delete AddSoundBtn;
	if(PlayListBtn)
		delete PlayListBtn;
	if(scrollbar)
		delete scrollbar;

	btnSoundOver = NULL;
	btnSoundClick = NULL;
	menu_selectionData = NULL;
	playlistImgData = NULL;
	trigA = NULL;
	playlistImg = NULL;
	AddSoundBtn = NULL;
	PlayListBtn = NULL;
	scrollbar = NULL;
	
	SetVisible(false);
}

void PlayList::InternalAddEntrie(const std::string &filepath)
{
	if(filepath.empty())
		return;

	if(FindFile(filepath.c_str()) < 0)
		FileList.push_back(filepath);
}

void PlayList::InternalRemoveEntrie(int pos)
{
	if(pos < 0 || pos >= (int) FileList.size())
		return;

	FileList.erase(FileList.begin()+pos);
}

void PlayList::InternalClearList()
{
	FileList.clear();
	std::vector<std::string>().swap(FileList);
	listOffset = 0;
	selectedItem = 0;
}

int PlayList::FindFile(const char * filepath)
{
	if(!filepath)
		return -1;

	for(int i = 0; i < (int)FileList.size(); i++)
	{
		if(strcasecmp(filepath, FileList[i].c_str()) == 0)
			return i;
	}

	return -1;
}

const char * PlayList::at(int pos)
{
	if(pos < 0 || pos >= (int) FileList.size())
		return NULL;

	return FileList[pos].c_str();
}

bool PlayList::ParsePath(const char * filepath)
{
	char currentPath[1024];
	sprintf(currentPath, "%s", filepath);

	char * pathptr = strrchr(currentPath, '/');
	if(pathptr)
	{
		pathptr++;
		pathptr[0] = 0;
	}
	else
		return false;

	DirList dir(currentPath, Settings.FileExtensions.GetAudio(), DirList::Files | DirList::CheckSubfolders);

	for(int i = 0; i < dir.GetFilecount(); ++i)
		InternalAddEntrie(dir.GetFilepath(i));
	
	return true;
}

void PlayList::OnListStateChange(GuiElement *sender, int state, int stateChan UNUSED)
{
	if(scrollbar->BoxIsHolded())
		goto jump;
	
	for(int i = 0; i < PAGE_SIZE && i < (int)FileList.size(); i++)
	{
		if(sender == ListBtn[i])
		{
			if(state == STATE_SELECTED)
			{
				selectedItem = i;
				break;
			}
		}
	}
	
  jump:
	
	if(ListBtn[selectedItem]->GetState() == STATE_DEFAULT)
		ListBtn[selectedItem]->SetState(STATE_SELECTED);
	
	scrollbar->SetSelectedItem(selectedItem);
}

void PlayList::OnListChange(int selItem, int selIndex)
{
	if(!IsVisible())
		return;
	
	static bool changing = false;
	if(changing)
		return;
	changing = true;
	
	bool reloadNeeded = false;
	if((listOffset != selIndex) || forceReload)
		reloadNeeded = true;
	forceReload = false;
	
	selectedItem = selItem;
	listOffset = selIndex;

	for(int i = 0; i < PAGE_SIZE && reloadNeeded; i++)
	{
		if(listOffset+i < (int) FileList.size())
		{
			if(ListBtn[i]->GetState() == STATE_DISABLED)
			{
				ListBtn[i]->SetVisible(true);
				ListBtn[i]->SetState(STATE_DEFAULT);
			}
			
			const char * filename = strrchr(FileList[listOffset+i].c_str(), '/');
			if(filename)
				filename++;
			
			ListBtnTxt[i]->SetText(filename);
			ListBtnTxtOver[i]->SetText(filename);
		}
		else
		{
			ListBtn[i]->SetVisible(false);
			ListBtn[i]->SetState(STATE_DISABLED);
		}
	}
	
	for(int i = 0; i < PAGE_SIZE; i++)
	{
		if(ListBtn[i]->GetState() == STATE_SELECTED && i != selItem)
			ListBtn[i]->SetState(STATE_DEFAULT);
	}
	
	reloadNeeded = false;
	changing = false;
}

void PlayList::OnPlayListChange()
{
	if((int)FileList.size() > PAGE_SIZE)
	{
		while(listOffset+PAGE_SIZE > (int) FileList.size())
			--listOffset;
	}
	
	selectedItem = LIMIT(selectedItem, 0, MIN((int)FileList.size(), PAGE_SIZE));
	
	if(!IsVisible())
		return;
	
	forceReload = true;
	OnListChange(selectedItem, listOffset);
	
	scrollbar->SetEntrieCount(FileList.size());
	
	scrollbar->SetSelectedItem(selectedItem);
	scrollbar->SetSelectedIndex(listOffset);
}

void PlayList::Draw()
{
	if(!IsVisible())
		return;

	playlistImg->Draw();

	if(Minimized)
		return;

	scrollbar->Draw();
	
	for(u32 i = 0; i < ListBtn.size(); i++)
		ListBtn[i]->Draw();
	
	this->UpdateEffects();
}

void PlayList::Update(GuiTrigger * t)
{
	if(!IsVisible())
		return;

	if(!Minimized)
	{
		bool ListChanged = false;
		
		if(t->Up())
		{
			if(selectedItem == 0 && listOffset > 0)
			{
				--listOffset;
				ListChanged = true;
			}
			else if(listOffset+selectedItem > 0)
			{
				--selectedItem;
				ListChanged = true;
			}
		}
		else if(t->Down())
		{
			if(listOffset+selectedItem + 1 < (int)FileList.size())
			{
				if(selectedItem == PAGE_SIZE-1)
				{
					//! move list down by 1
					listOffset++;
				}
				else
				{
					selectedItem++;
				}
				ListChanged = true;
			}
		}
		else if(t->Left())
		{
			if(listOffset > 0)
			{
				listOffset -= PAGE_SIZE;
				if(listOffset < 0)
				{
					listOffset = 0;
					selectedItem = 0;
				}
				ListChanged = true;
			}
		}
		else if(t->Right())
		{
			if(listOffset < MAX(0, ((int)FileList.size()-PAGE_SIZE)))
			{
				listOffset += PAGE_SIZE;
				if(listOffset > (int)FileList.size()-PAGE_SIZE)
				{
					listOffset = MAX((int)FileList.size()-PAGE_SIZE, 0);
					selectedItem = LIMIT(PAGE_SIZE-1, 0, (int)FileList.size()-1);
				}
				ListChanged = true;
			}
		}
		
		if(ListChanged)
		{
			forceReload = true;
			OnListChange(selectedItem, listOffset);
			
			scrollbar->SetSelectedItem(selectedItem);
			scrollbar->SetSelectedIndex(listOffset);
		}
	}
	
	GuiFrame::Update(t);
}
